package fr.ul.ade.secunde.ui;

import java.text.ParseException;
import java.text.SimpleDateFormat;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.BaseColumns;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import com.actionbarsherlock.app.SherlockListFragment;

import fr.ul.ade.R;
import fr.ul.ade.secunde.database.AdeWebContract;
import fr.ul.ade.secunde.ui.ScheduleListFragment.ScheduleQuery;
import fr.ul.ade.secunde.util.Bundles;
import fr.ul.ade.secunde.util.LessonConstants;
import fr.ul.ade.secunde.util.UiUtils;

/**
 * Fragment affichant la liste des cours pour un emploi du temps donné.
 * 
 * @author Michaël Schnell
 * @since Jul 2, 2013
 * 
 * @version 1.5
 */
public class LessonListFragment
		extends SherlockListFragment
		implements LoaderCallbacks<Cursor>, OnClickListener {

	protected ScheduleActionListener scheduleListener;

	protected LessonActionListener lessonListener;

	protected CursorAdapter listAdapter;

	protected long scheduleId;

	public static LessonListFragment newInstance(long scheduleId) {
		LessonListFragment fragment = new LessonListFragment();
		Bundle args = new Bundle();
		args.putLong(Bundles.SCHEDULE_ID, scheduleId);
		fragment.setArguments(args);
		return fragment;
	}

	// --------------------------------------------------------
	//
	// Création
	//
	// --------------------------------------------------------

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		return UiUtils.getListView(inflater, container);
	}

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		Activity activity = this.getActivity();
		this.scheduleListener = (ScheduleActionListener) activity;

		this.listAdapter = new LessonListAdapter(activity);
		this.setListAdapter(this.listAdapter);

		Bundle args = this.getArguments();
		// Dans le cas d'un mobile, l'identifiant de l'emploi du temps est passé
		// par l'intent.
		if (args == null || !args.containsKey(Bundles.SCHEDULE_ID)) {
			this.scheduleId = this.getActivity().getIntent()
					.getLongExtra(Bundles.SCHEDULE_ID, 0);
		} else {
			this.scheduleId = args.getLong(Bundles.SCHEDULE_ID);
		}

		this.getLoaderManager().initLoader(LessonQuery._TOKEN, null, this);
		// FIXME Ajout d'un écouteur pour les modifications des données
		activity.getContentResolver()
				.registerContentObserver(AdeWebContract.Lesson.buildDisplayUri(this.scheduleId),
						true,
						this.mLessonObserver);
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		this.getActivity().getContentResolver()
				.unregisterContentObserver(this.mLessonObserver);
	}

	// --------------------------------------------------------
	//
	// Chargement des données
	//
	// --------------------------------------------------------

	@Override
	public Loader<Cursor> onCreateLoader(int id, Bundle args) {
		Uri lessonUri = AdeWebContract.Lesson.buildDisplayUri(this.scheduleId);

		return new CursorLoader(this.getActivity(), lessonUri,
				LessonQuery.PROJECTION, null, null,
				AdeWebContract.Lesson.SORT_BY_ORDER);
	}

	@Override
	public void onLoaderReset(Loader<Cursor> loader) {
		/* Nothing to be done. */
	}

	@Override
	public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
		if (this.getActivity() == null) {
			return;
		}// if
		this.listAdapter.changeCursor(data);
		if (data.getCount() == 0) {
			UiUtils.addEmptyView(this.getActivity().getLayoutInflater(),
					this.getView(),
					this,
					R.string.lesson_list_empty_text);
		}

		final View view = this.getView();
		view.findViewById(R.id.list_loading_progress_bar)
				.setVisibility(View.GONE);
	}

	// --------------------------------------------------------
	//
	// Traitement des évènements
	//
	// --------------------------------------------------------

	@Override
	public void onListItemClick(ListView l, View v, int position, long id) {
		this.lessonListener.showLessonDetails(this.scheduleId, id);
	}

	@Override
	public void onClick(View v) {
		final FragmentActivity activity = this.getActivity();
		Intent updateIntent = new Intent(activity.getResources()
				.getString(R.string.intent_action_update_schedule));
		updateIntent.putExtra(Bundles.SCHEDULE_ID, this.scheduleId);
		activity.startService(updateIntent);
	}

	// FIXME Register and unregister listener
	private final ContentObserver mLessonObserver = new ContentObserver(
			new Handler()) {

		@Override
		public void onChange(boolean selfChange) {
			LessonListFragment.this.getLoaderManager()
					.restartLoader(ScheduleQuery._TOKEN,
							null,
							LessonListFragment.this);
		}

		@Override
		public void onChange(boolean selfChange, Uri uri) {
			this.onChange(selfChange);
		}
	};

	// --------------------------------------------------------
	//
	// Données chargées
	//
	// --------------------------------------------------------

	protected interface LessonQuery {
		final int _TOKEN = 0x1;

		final String[] PROJECTION = { BaseColumns._ID,
				AdeWebContract.Lesson.LESSON_TITLE,
				AdeWebContract.Lesson.LESSON_ROOMS,
				AdeWebContract.Lesson.LESSON_START_TIME,
				AdeWebContract.Lesson.LESSON_END_TIME,
				AdeWebContract.Lesson.LESSON_DATE,
				AdeWebContract.Lesson.LESSON_CHANGE,
				AdeWebContract.Lesson.LESSON_ORDER,
				AdeWebContract.Lesson.LESSON_DELETED,
				AdeWebContract.Lesson.LESSON_TYPE };

		final int _ID = 0;
		final int LESSON_TITLE = 1;
		final int LESSON_ROOMS = 2;
		final int LESSON_START_TIME = 3;
		final int LESSON_END_TIME = 4;
		final int LESSON_DATE = 5;
		final int LESSON_CHANGE = 6;
		final int LESSON_ORDER = 7;
		final int LESSON_DELETED = 8;
		final int LESSON_TYPE = 9;
	}

	// --------------------------------------------------------
	//
	// Adaptateur pour la liste
	//
	// --------------------------------------------------------

	protected static class LessonListAdapter
			extends CursorAdapter {

		/**
		 * Format d'affichage pour les dates.
		 */
		private static final String DATE_DISPLAY_FORMAT = "E, d MMMM"; //$NON-NLS-1$

		/**
		 * Nombre de types d'items gérès par cet adaptateur.
		 */
		private static final int N_LESSON_TYPES = 6;

		/**
		 * Parseur pour les dates extraites des cours.
		 */
		private final static SimpleDateFormat parser = new SimpleDateFormat(
				LessonConstants.LESSON_DATE_FORMAT);

		/**
		 * Formatte les dates aux format utilisé pour l'affichage.
		 */
		private final static SimpleDateFormat formatter = new SimpleDateFormat(
				DATE_DISPLAY_FORMAT);

		public LessonListAdapter(Context context) {
			super(context, null, 0 /* no flags set. */);
		}

		@Override
		public boolean areAllItemsEnabled() {
			return false;
		}

		@Override
		public boolean isEnabled(int position) {
			return this.getItemViewType(position) == LessonConstants.LESSON_TYPE_LESSON;
		}

		@Override
		public int getViewTypeCount() {
			return N_LESSON_TYPES;
		}

		@Override
		public int getItemViewType(int position) {
			final Cursor item = (Cursor) this.getItem(position);
			return item.getInt(LessonQuery.LESSON_TYPE);
		}

		@Override
		public View newView(Context context, Cursor cursor, ViewGroup parent) {
			Log.d("[newView]", "View type: "+cursor.getInt(LessonQuery.LESSON_TYPE));
			
			LayoutInflater inflater = (LayoutInflater) context
					.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			switch (cursor.getInt(LessonQuery.LESSON_TYPE)) {
			case LessonConstants.LESSON_TYPE_DATE:
				return newDateView(parent, inflater);

			case LessonConstants.LESSON_TYPE_SMALL_BREAK:
			case LessonConstants.LESSON_TYPE_BREAK:
				return newBreakView(parent, inflater);

			case LessonConstants.LESSON_TYPE_LESSON:
			case LessonConstants.LESSON_TYPE_MODIFIED_LESSON:
			case LessonConstants.LESSON_TYPE_DELETED_LESSON:
			default:
				return newLessonView(parent, inflater);
			}
		}

		/**
		 * Crée un item pour l'affichage d'un cours normal.
		 * 
		 * @param parent
		 *            the parent to which the new view is attached to
		 * @param inflater
		 * @return {@link View} pour l'item de type cours normal à afficher
		 */
		private static View newLessonView(ViewGroup parent,
				LayoutInflater inflater) {
			final View view = inflater
					.inflate(R.layout.lesson_list_item_lesson, parent, false);

			final LessonViewHolder holder = new LessonViewHolder();
			holder.startTime = (TextView) view
					.findViewById(R.id.lesson_item_start_time);
			holder.endTime = (TextView) view
					.findViewById(R.id.lesson_item_end_time);
			holder.title = (TextView) view.findViewById(R.id.lesson_item_title);
			holder.rooms = (TextView) view.findViewById(R.id.lesson_item_room);
			holder.change = (ImageView) view
					.findViewById(R.id.lesson_item_change);
			view.setTag(holder);

			return view;
		}

		/**
		 * Crée un item pour l'affichage d'une date.
		 * 
		 * @param parent
		 *            the parent to which the new view is attached to
		 * @param inflater
		 * @return {@link View} pour l'item de type date à afficher
		 */
		private static View newDateView(ViewGroup parent,
				LayoutInflater inflater) {
			return inflater.inflate(R.layout.lesson_list_item_date,
					parent,
					false);
		}

		/**
		 * Crée un item pour l'affichage d'un séparateur.
		 * 
		 * @param parent
		 *            the parent to which the new view is attached to
		 * @param inflater
		 * @return {@link View} pour l'item de type séparateur à afficher
		 */
		private static View newBreakView(ViewGroup parent,
				LayoutInflater inflater) {
			return inflater.inflate(R.layout.lesson_list_item_separator,
					parent,
					false);
		}

		@Override
		public void bindView(View view, Context context, Cursor cursor) {
			switch (cursor.getInt(LessonQuery.LESSON_TYPE)) {
			case LessonConstants.LESSON_TYPE_DATE:
				bindDateView(view, cursor);
				break;

			case LessonConstants.LESSON_TYPE_SMALL_BREAK:
			case LessonConstants.LESSON_TYPE_BREAK:
				break;

			case LessonConstants.LESSON_TYPE_LESSON:
			case LessonConstants.LESSON_TYPE_MODIFIED_LESSON:
			case LessonConstants.LESSON_TYPE_DELETED_LESSON:
			default:
				bindLessonView(view, cursor);
			}
		}

		/**
		 * See {@link CursorAdapter#bindView(View, Context, Cursor)}.
		 */
		private static void bindDateView(View view, Cursor cursor) {
			UiUtils.setTextViewContent(view,
					R.id.lesson_list_date,
					getFormattedDate(cursor.getString(LessonQuery.LESSON_DATE)));
		}

		/**
		 * See {@link CursorAdapter#bindView(View, Context, Cursor)}.
		 */
		private static void bindLessonView(View view, Cursor cursor) {
			final LessonViewHolder holder = (LessonViewHolder) view.getTag();
			holder.startTime.setText(cursor
					.getString(LessonQuery.LESSON_START_TIME));
			holder.endTime.setText(cursor
					.getString(LessonQuery.LESSON_END_TIME));
			holder.title.setText(cursor.getString(LessonQuery.LESSON_TITLE));
			holder.rooms.setText(cursor.getString(LessonQuery.LESSON_ROOMS));
			boolean hasChange = (cursor.getInt(LessonQuery.LESSON_CHANGE) != 0);
			holder.change.setVisibility(hasChange ? View.VISIBLE : View.GONE);
		}

		private static String getFormattedDate(String date) {
			try {
				return formatter.format(parser.parse(date));
			} catch (ParseException e) {
				// TODO Review error message
				Log.d("[getFormattedDate]", "Invalid date.", e);
			}
			return null;
		}

	}// class LessonListAdapter

	/**
	 * Classe auxiliaire utilisée pour accéder aux champs d'un item de la liste.
	 * Stocke les résultats des appels à {@link View#findViewById(int)} afin de
	 * de ne devoir rechercher les champs à chaque mise à jour de la vue.
	 * 
	 * @author Michaël Schnell
	 * @since Jul 19, 2013
	 * 
	 * @version 1.3
	 */
	static class LessonViewHolder {
		TextView startTime;
		TextView endTime;
		TextView title;
		TextView rooms;
		ImageView change;
	}

}// class LessonListFragment